import sys
sys.path.append('protosGen')

from config.constants import Constants
from common.roll_log import init_roll_log
import threading
from protosGen.common_pb2 import *
from protosGen.marketGateway_pb2 import *
from protosGen.orderreporting_pb2 import *
import time
from threading import Thread
import logging

strategy_name = "MA Crossing Strategy"
logger = init_roll_log(strategy_name + ".log",logging.INFO)

# strategy params
symbol = Symbol(
    exchangeType = Constants.Exchanges.HUOBI.value,
    baseCurrency = "btc",
    quoteCurrency = "usdt")
quantity = '0.01'

class MaStrategy:
    def __init__(self, market_stub, order_stub):
        self.shortMaCal = MaCalculator(5)
        self.longMaCal = MaCalculator(30)
        self.market_stub = market_stub
        self.order_stub = order_stub
        self.longOrders = dict()
        self.shortOrders = dict()
        self.orderHistory = dict() # todo, need to handle the memory overflow
        self._stop_event = threading.Event()

        self.tickHandlerThread = Thread(target=self.__subAndHandleTick, name = 'MaStrategyHandleTick')
        self.orderHandlerThread = Thread(target=self.__subAndHandleOrder, name = 'MaStrategyHandleOrder')

    def start(self):
        self.tickHandlerThread.start()
        self.orderHandlerThread.start()

    def stop(self):
        self._stop_event.set()
        self.orderHandlerThread.join(5)
        self.tickHandlerThread.join(5)


    def __subAndHandleTick(self):
        tickResponses = self.market_stub.subTick(self.__genSubTickMsg())
        self.__handleTick(tickResponses)

    def __genSubTickMsg(self):
        def threadSleep():
            while not self._stop_event.is_set():
                time.sleep(1)
        yield ReqSubTick(symbol = symbol, isUnSub = False)
        yield threadSleep()
        

    def __handleTick(self, responses):
        for response in responses:
            if response.CRsp.isSuccess:
                tick = response.data
                self.shortMaCal.pushValue(tick.close)
                self.longMaCal.pushValue(tick.close)
                self.__doTrade(float(tick.close))

    def __doTrade(self, price):
        if self.longMaCal.getLastMa():
            if self.shortMaCal.getLastMa() <= self.longMaCal.getLastMa() and self.shortMaCal.getMa() > self.longMaCal.getMa():
                logger.info("Crossing up, do long!")

                for orderId in list(self.shortOrders):
                    logger.info("Cancel order:" + orderId)
                    cancel_order_response = self.order_stub.cancelOrder(CancelOrderRequest(
                        orderId = orderId))
                    if cancel_order_response.data.cancelstatus == 'success':
                        del(self.shortOrders[orderId])
                    elif cancel_order_response.CRsp.isSuccess:
                        self.shortOrders[orderId] = Constants.OrderStatus.INSERT_CANCEL_SUCCESS
                    else:
                        logger.info("Insert cancel short order failed, errCode: " + cancel_order_response.CRsp.errCode + ". errMsg: " + cancel_order_response.CRsp.errMsg)

                order_response = self.order_stub.insertOrder(InsertOrderRequest(
                    subaccountCode = Constants.SUBACCOUNT_CODE_HUOBI,
                    symbol = symbol,
                    quantity = quantity,
                    price = str(price),
                    type = Constants.OrderType.LIMIT.value,
                    side = Constants.OrderSide.BUY.value))
                if order_response.CRsp.isSuccess:
                    self.longOrders[order_response.orderId] = Constants.OrderStatus.INSERT_SUCCESS
                else:
                    logger.info("Insert buy order failed, errCode: " + order_response.CRsp.errCode + ". errMsg: " + order_response.CRsp.errMsg)

            elif self.shortMaCal.getLastMa() >= self.longMaCal.getLastMa() and self.shortMaCal.getMa() < self.longMaCal.getMa():
                logger.info("Crossing down, do short!")

                for orderId in list(self.longOrders):
                    logger.info("Cancel order:" + orderId)
                    cancel_order_response = self.order_stub.cancelOrder(CancelOrderRequest(
                        orderId = orderId))
                    if cancel_order_response.data.cancelstatus == 'success':
                        del(self.longOrders[orderId])
                    elif cancel_order_response.CRsp.isSuccess:
                        self.longOrders[orderId] = Constants.OrderStatus.INSERT_CANCEL_SUCCESS
                    else:
                        logger.info("Insert cancel long order failed, errCode: " + cancel_order_response.CRsp.errCode + ". errMsg: " + cancel_order_response.CRsp.errMsg)

                order_response = self.order_stub.insertOrder(InsertOrderRequest(
                    subaccountCode = Constants.SUBACCOUNT_CODE_HUOBI,
                    symbol = symbol,
                    quantity = quantity,
                    price = str(price),
                    type = Constants.OrderType.LIMIT.value,
                    side = Constants.OrderSide.SELL.value))
                if order_response.CRsp.isSuccess:
                    self.shortOrders[order_response.orderId] = Constants.OrderStatus.INSERT_SUCCESS
                else:
                    logger.info("Insert sell order failed, errCode: " + order_response.CRsp.errCode + ". errMsg: " + order_response.CRsp.errMsg)

            else:
                logger.debug("No crossing moving, do nothing.")
        else:
            logger.info("Moving average data not ready.")

    def __subAndHandleOrder(self):
        orderResponses = self.order_stub.subOrder(self.__genSubOrderMsg())
        self.__handleOrder(orderResponses)

    def __genSubOrderMsg(self):
        def threadSleep():
            while not self._stop_event.is_set():
                time.sleep(1)
        yield ReqSubOrder(subaccountCode = Constants.SUBACCOUNT_CODE_HUOBI)
        yield threadSleep()

    def __handleOrder(self, responses):
        for rtnSubOrder in responses:
            order = rtnSubOrder.order
            logger.info("Order Status updated, orderStatus:" + str(order.orderStatus) + ", exchangeOrderId:" + str(order.exchangeOrderId) + ", orderId:" + str(order.orderId))
            if order.orderStatus in ['canceled', 'filled', 'exch_rejected', 'tg_connection_error','partially_canceled']:
                self.orderHistory[order.orderId] = order
                if order.side == Constants.OrderSide.BUY.value and order.orderId in self.longOrders:
                    del(self.longOrders[order.orderId])
                elif order.side == Constants.OrderSide.SELL.value and order.orderId in self.shortOrders:
                    del(self.shortOrders[order.orderId])


class MaCalculator:
    def __init__(self, period):
        self.period = period
        self.values = []
        self.ma = None
        self.lastMa = None

    def pushValue(self, value):
        self.values.append(float(value))
        while len(self.values) > self.period:
            self.values.pop(0)
        self.lastMa = self.ma
        self.ma = self.__calcMa()

    def __calcMa(self):
        sum = 0
        if len(self.values) < self.period:
            return None
        else:
            for value in self.values:
                sum += value
        return sum/len(self.values)

    def getMa(self):
        return self.ma

    def getLastMa(self):
        return self.lastMa